package list1;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;

/**
 *  Implementation of lists, using singly linked elements.
 *
 * @author     G. Peck
 * @created    April 27, 2002
 */
public class SinglyLinkedList{
  private ListNode first;  // first element
  private ListNode last;

  /**
   *  Constructor for the SinglyLinkedList object
   *  Generates an empty list.
   */
  public SinglyLinkedList(){
    first = null;
    last = null;
  }
  
  /**
   * Loads a data file as Items
   * @param file the location of the file
   * @return true if loads, false if it doesn't.
   */
  public boolean loadData(String file)
    {
    Scanner input = null;
    try
        {
        input = new Scanner(new File(file));
        while (input.hasNextInt())
            insert(new Item(input.nextInt(), input.nextInt()));
        }
    catch (FileNotFoundException ex)
        {
        System.out.println(ex);
        }
    return false;
    }

  /**
   *  Returns the first element in this list.
   *
   * @return  the first element in the linked list.
   */
  public Object getFirst(){
    if (first == null){
      throw new NoSuchElementException();
    }
    else
      return first.getValue();
  }
  
  /**
   * Returns the last element in this list.
   * 
   * @return the last element in the linked list.
   */
  public Object getLast()
    {
    if (last == null)
        throw new NoSuchElementException();
    else
        return last.getValue();
    }
  
  /**
   * Returns an element at a given index
   * @param index the index to look for
   * @return an element at a given index
   */
    @SuppressWarnings("empty-statement")
  public Object get(int index)
    {
    if (index > size() || index < 0)
        return null;
    else
        {
        ListNode node = first;
        for (int i = 0;i < index;i++, node = node.getNext());
        return node;
        }
    }
  
  /**
   *  Inserts the value using Comparable
   * 
   * @param value
   * @return false if its not Comparable, true if it is
   */
  public boolean insert(Object value)
    {
    if (!(value instanceof Comparable))
        return false;
    if (first == null)
        addFirst(value);
    else
        insert((Comparable)value, null, first);
    return true;
    }
    
    /**
     * A helper recursive function thats helps insert the element
     * @param value a comparable object
     * @param back the trailing node
     * @param forward the leading node
     */
    private void insert(Comparable value, ListNode back, ListNode forward)
        {
        if (forward == null)
            {
            addLast(value);
            return;
            }
        if (value.compareTo(forward.getValue()) < 0)
            {
            if (back != null)
                back.setNext(new ListNode(value, forward));
            else
                addFirst(value);
            }
        else
            insert(value, forward, forward.getNext());
        }
    
    /**
     * Returns a ListNode with the same value as the parameter
     * 
     * @param value the value to look for in the linked list
     * @return a ListNode with the value parameter
     */
    public ListNode find(Object value)
        {
        for (ListNode i = first; i != null; i = i.getNext())
            if (value.equals(i.getValue()))
                return i;
        return null;
        }
  

    /**
     * Removes a ListNode with the value equivelent to the parameter value
     * @param value the value to look for
     * @return ListNode with the value equal to the parameter
     */
    public ListNode remove(Object value)
        {
        for (ListNode temp = first, back = null ;temp.getNext() != null;
            back = temp, temp = temp.getNext())
            if (temp.getValue().equals(value))
                {
                if (back != null)
                    back.setNext(temp.getNext());
                else
                    first = temp.getNext();
                return temp;
                }
        return null;
        }
    
  /**
   * Returns the size of the list.
   * @return  the size of the linked list.
   */
    @SuppressWarnings("empty-statement")
  public int size()
    {
    int size = 0;
    for (ListNode temp = first;temp != null; size++, temp = temp.getNext());
    return size;
    }

    /**
     * Clears the Linked List by setting the two pointers to null
     */
    public void clear()
        {
        System.out.println("Now Clearing Linked List");
        printList();
        first = null;
        last = null;
        }
    
  /**
   *  Inserts the given element at the beginning of this list.
   *
   * @param  value  the element to be inserted at the beginning of this list.
   */
  public void addFirst(Object value){
    // note the order that things happen:
    // head is parameter, then assigned
    first = new ListNode(value, first);
    last = last == null? first: last;
  }
  
  /**
   * Inserts the given element at the end of this list.
   * 
   * @param value  the element to be inserted at the end of this list.
   */
  public void addLast(Object value)
    {
    if (first == null)
        addFirst(value);
    else
        {
        last.setNext(new ListNode(value));
        last = last.getNext();
        }
    }

  /**
   *  Print the contents of the entire linked list
   */
  public void printList(){
    ListNode temp = first;// start from the first node
    while (temp != null){
      System.out.println(temp.getValue() + " ");
      temp = temp.getNext();// go to next node
    }
    System.out.println();
  }
  
  /**
   * Prints the contents of the ntire linked list backwards
   */
  public void printBackward()
    {
    printBack(first);
    }
  
  /**
   * Helper Class that helps print backward
   * @param the first item of the node
   */
  protected void printBack(ListNode first)
    {
    if (first.getNext() != null)
        printBack(first.getNext());
    System.out.print(first.getValue() + " ");
    }

  /**
   *  Returns a string representation of this list. The string
   *  representation consists of the list's elements in order,
   *  enclosed in square brackets ("[]"). Adjacent elements are
   *  separated by the characters ", " (comma and space).
   *
   * @return    string representation of this list
   */
  public String toString(){
    String s = "[";

    ListNode temp = first;  // start from the first node
    while (temp != null){
      s += temp.getValue(); // append the data
      temp = temp.getNext();      // go to next node
      if (temp != null)
        s += ", ";
    }
    s += "]";
    return s;
  }
}
